package com.google.guava.eventbus.monitor;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.google.common.eventbus.EventBus;

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

/**
 * @author fuc
 * @create 2020-10-06
 */
public class DirectoryTargetMonitor implements TargetMonitor {
  private static final Log log = LogFactory.get();

  private WatchService watchService;

  private final EventBus eventBus;

  private final Path path;

  private volatile boolean start = false;

  public DirectoryTargetMonitor(final EventBus eventBus, final String targetPath) {
    this(eventBus, targetPath, "");
  }

  public DirectoryTargetMonitor(
      final EventBus eventBus, final String targetPath, final String... morePaths) {
    this.eventBus = eventBus;
    this.path = Paths.get(targetPath, morePaths);
  }

  @Override
  public void startMonitor() throws Exception {
    this.watchService = FileSystems.getDefault().newWatchService();
    this.path.register(
        watchService,
        StandardWatchEventKinds.ENTRY_CREATE,
        StandardWatchEventKinds.ENTRY_DELETE,
        StandardWatchEventKinds.ENTRY_MODIFY);

    log.info("The directory [{}] is monitoring", path);

    this.start = true;
    while (start) {
      WatchKey watchKey = null;
      try {
        watchKey = watchService.take();
        watchKey
            .pollEvents()
            .forEach(
                event -> {
                  WatchEvent.Kind<?> kind = event.kind();
                  Path path = (Path) event.context();
                  Path child = DirectoryTargetMonitor.this.path.resolve(path);
                  eventBus.post(new FileChangeEvent(child, kind));
                });
      } catch (InterruptedException e) {
        e.printStackTrace();
        this.start = false;
      } finally {
        if (watchKey != null) {
          watchKey.reset();
        }
      }
    }
  }

  @Override
  public void stopMonitor() throws Exception {
    log.info("The directory [{}] monitor will be stop...", path);
    Thread.currentThread().interrupt();
    this.start = false;
    this.watchService.close();
    log.info("The directory [{}] monitor will be done.", path);
  }
}
